#coding=utf-8
import time,io
import datetime,time,uuid, base64, random,json,requests, hmac
from hashlib import sha1
from db.models import InvoiceItem
#sys.stdout = sys.stderr

#https://open.jss.com.cn/#/api-doc/common-api?id=100607

try:
    from base.models import *
except:
    pass

######################################################
#公共底层接口
######################################################
def get_sign(secret, appkey, senid, nonce, content, timestamp):
    # 生成并返回签名
    param = 'a=services&l=v1&p=open&k=' + appkey + '&i=' + senid + '&n=' + nonce + '&t=' + timestamp + '&f=' + content
    hmac_code = hmac.new(secret.encode(), param.encode(), sha1).digest()
    return base64.b64encode(hmac_code).decode()

def send_request(request_url, senid, appkey, app_secret, token, taxnum, method, content):
    """
    调用诺诺开放平台API
    发送HTTP POST请求 <同步>
    """
    timestamp = str(int(time.time()))  # 时间戳
    nonce = str(random.randint(10000, 1000000000))  # 随机正整数
    sign = get_sign(app_secret, appkey, senid, nonce, content, timestamp)  # 签名

    request_url = request_url + '?senid=' + senid + '&nonce=' + nonce + '&timestamp=' + timestamp + '&appkey=' + appkey
    head = {'Content-type': 'application/json'}
    head.update({'X-Nuonuo-Sign': sign})
    head.update({'accessToken': token})
    head.update({'userTax': taxnum})
    head.update({'method': method})
    #print u'--------------head-------------'
    #print head
    requestdata = content.encode("utf-8")
    r = requests.post(request_url, data=requestdata, headers=head)
    return r
    
def callNstApi(app, api_method, inXmlInner): #inXmlInner明文；inXmlWithOuter密文
    #print app.nst_url, app.nst_appKey, app.nst_appSecret, app.nst_accessToken, app.XHF_NSRSBH, api_method
    #print(u'--------------请求报文-------------')
    #print(inXmlInner)
    
    senid = uuid.uuid4().hex
    
    
    response = send_request(app.nst_url, senid, app.nst_appKey, app.nst_appSecret, app.nst_accessToken, app.XHF_NSRSBH, api_method, inXmlInner)
    
    outXml = response.text if response else None
    #print(u'--------------返回报文-------------')
    #print(outXml)
    
    return response.json()    

######################################################
#发票开具
######################################################
def fpkjInxml(app, kpsq):
    #kpsq.XHF_NSRSBH = '339901999999142'
    #kpsq.GHF_SJ = '18918888599'
    
    #0：专用发票 1：废旧物资发票、 2：普通发票、11-货运发票、41-卷票普通发票、51-电子普通发票
    if kpsq.InfoKind == 0:
        invoiceLine = 's'
    elif kpsq.InfoKind == 2:
        invoiceLine = 'c'
    elif kpsq.InfoKind == 51:
        invoiceLine = 'p'
    elif kpsq.InfoKind == 61: #全电专票
        invoiceLine = 'bs' #bs:电子发票(增值税专用发票)-即数电专票(电子),
    elif kpsq.InfoKind == 62: #全电普票
        invoiceLine = 'pc' #pc:电子发票(普通发票)-即数电普票(电子),
    else:
        invoiceLine = 'p'
    
    
    #es:数电纸质发票(增值税专用发票)-即数电专票(纸质);
    #ec:数电纸质发票(普通发票)-即数电普票(纸质)
        
        
    tsfs = -1
    if not kpsq.GHF_EMAIL and kpsq.GHF_SJ:
        tsfs = 2
    elif kpsq.GHF_EMAIL and not kpsq.GHF_SJ and app.emailSendBy==0:
        tsfs = 0
    elif kpsq.GHF_SJ and not kpsq.GHF_EMAIL:
        tsfs = 1
    inXml = {
        'buyername': kpsq.GHFMC, #购货方名称
        'buyerTaxNum': (kpsq.GHF_NSRSBH if kpsq.GHF_NSRSBH is not None else ''), #购货方识别号
        'buyerTel': (kpsq.GHF_SJ if kpsq.GHF_SJ is not None else ''), #购货方手机
        'buyerAddress': (kpsq.GHF_DZ if kpsq.GHF_DZ is not None else ''), #购货方地址
        'buyerAccount': (kpsq.GHF_YHZH if kpsq.GHF_YHZH is not None else ''), #购货方银行、账号
        #'buyerTel': (kpsq.GHF_GDDH if kpsq.GHF_GDDH is not None else ''), #购货方固定电话
        'orderNo': kpsq.FPQQLSH, #订单号
        'invoiceDate': kpsq.insertDate.strftime('%Y-%m-%d %H:%M:%S'),
        'salerAccount': kpsq.XHF_YHZH, #销货方银行账号
        'salerTel': kpsq.XHF_DH, #销货方电话
        'salerAddress': kpsq.XHF_DZ, #销货方地址
        'salerTaxNum': kpsq.XHF_NSRSBH, #销货方识别号
        'invoiceType': kpsq.KPLX, #开票类型 1	正票 2	红票  
        'remark': (kpsq.BZ if kpsq.BZ is not None else ''), #备注
        'clerk': kpsq.KPY, #开票员
        'payee': (kpsq.SKY if kpsq.InfoKind not in [61,62,'61','62'] else ''), #收款员
        'checker': (kpsq.FHR if kpsq.InfoKind not in [61,62,'61','62'] else ''), #复核人
        'invoiceCode': (kpsq.YFP_DM if kpsq.YFP_DM else ''), #原发票代码
        'invoiceNum': (kpsq.YFP_HM if kpsq.YFP_HM else ''), #原发票号码
        'pushMode': tsfs, #推送方式:-1, 不推 送 ;0,邮箱 ;1,手机 (默 认);2,邮箱+手机
        'email': (kpsq.GHF_EMAIL[:50] if kpsq.GHF_EMAIL is not None and app.emailSendBy==0 else ''), #购货方邮箱
        'listFlag': '0', #清单标志 默认为0
        'listName': '', #清单项目名称
        'proxyInvoiceFlag': '0', #代开标志 :0 非代开 ;1 代开。
        #'deptid': '',
        #'clerkId': '', #开票员id （诺系统中的 id ）
        'invoiceLine': invoiceLine, #发票种类， p电子增值税普通发票
        #'specificFactor': '1' if kpsq.cpybz == '1' else '0', #成品油标志： 0非成品油， 1成品油
        'billInfoNo':'', #专票冲红时，此项必填. 红字信息表编号
        'invoiceDetail': [],
    }
    kpsqGoodss = InvoiceItem.objects.filter(kpsq=kpsq)
    for kpsqGoods in kpsqGoodss:
        inXml['invoiceDetail'].append({
            'goodsname': kpsqGoods.XMMC, #项目名称
            'num': (kpsqGoods.XMSL if kpsqGoods.XMSL is not None else ''), #项目数量
            'price': (kpsqGoods.XMDJ if kpsqGoods.XMDJ is not None else ''),  #项目单价
            'withTaxFlag': kpsqGoods.HSBZ, #含税标志
            'taxRate': kpsqGoods.SL, #税率
            'specType': (kpsqGoods.GGXH if kpsqGoods.GGXH is not None else ''), #规格型号
            'unit': (kpsqGoods.XMDW if kpsqGoods.XMDW is not None else ''), #项目单位
            'goodsCode': kpsqGoods.SPBM, #商品编码
            'selfCode': '',
            'invoiceLineProperty': kpsqGoods.FPHXZ, #发票行性质
            'favouredPolicyFlag': (1 if kpsqGoods.YHZCBS == '1' else 0), #优惠政策标识
            'favouredPolicyName': kpsqGoods.ZZSTSGL,
            'zeroRateFlag': kpsqGoods.LSLBS, #零税率标识
            'deduction': (kpsqGoods.KCE if kpsqGoods.KCE not in [None, ''] else '0'), #扣除额
            #'taxfreeamt': kpsqGoods.XMJE, #不含税金额
            'tax': (kpsqGoods.SE if kpsqGoods.SE is not None else ''), #税额
            #'taxamt': kpsqGoods.XMJE, #含税金额
         })
         
    #print inXml
    
    inXml = { 
        "order" : inXml,
    }
    return inXml

#红冲发票    
def fpqdredInxml(app, kpsq):
    #0：专用发票 1：废旧物资发票、 2：普通发票、11-货运发票、41-卷票普通发票、51-电子普通发票
    if kpsq.InfoKind == 0:
        invoiceLine = 's'
    elif kpsq.InfoKind == 2:
        invoiceLine = 'c'
    elif kpsq.InfoKind == 51:
        invoiceLine = 'p'
    elif kpsq.InfoKind == 61: #全电专票
        invoiceLine = 'bs' #bs:电子发票(增值税专用发票)-即数电专票(电子),
    elif kpsq.InfoKind == 62: #全电普票
        invoiceLine = 'pc' #pc:电子发票(普通发票)-即数电普票(电子),
    else:
        invoiceLine = 'p'
    
    
    #es:数电纸质发票(增值税专用发票)-即数电专票(纸质);
    #ec:数电纸质发票(普通发票)-即数电普票(纸质)
        
        
    tsfs = -1
    if not kpsq.GHF_EMAIL and kpsq.GHF_SJ:
        tsfs = 2
    elif kpsq.GHF_EMAIL and not kpsq.GHF_SJ and app.emailSendBy==0:
        tsfs = 0
    elif kpsq.GHF_SJ and not kpsq.GHF_EMAIL:
        tsfs = 1
    inXml = {
        'billId':kpsq.FPQQLSH, #红字确认单申请号，需要保持唯一，不传的话系统自动生成一个
        'blueInvoiceLine':invoiceLine, #对应蓝票发票种类: bs:电子发票(增值税专用发票)， pc:电子发票(普通发票)，es:全电纸质发票(增值税专用发票)， ec:全电纸质发票(普通发票)，p:普通发票（电票），c:普通发票（纸票），s:专用发票，b:增值税电子专用发票
        'applySource':'0',#申请方（录入方）身份： 0 销方 1 购方
        'blueInvoiceNumber':(kpsq.YFP_HM if kpsq.YFP_HM else ''), #原发票号码,
        'blueInvoiceCode':(kpsq.YFP_DM if kpsq.YFP_DM else ''), #原发票代码
        'blueElecInvoiceNumber':(kpsq.YFP_HM if kpsq.YFP_HM else ''), #对应蓝字数电票号码（数电普票、数电专票、数纸普票、数纸专票都需要传，蓝票是增值税发票时不传）,
        'sellerTaxNo':kpsq.XHF_NSRSBH,#销方税号
        'sellerName':kpsq.XHFMC,#销方名称，申请说明为销方申请时可为空
        'buyerTaxNo':(kpsq.GHF_NSRSBH if kpsq.GHF_NSRSBH is not None else ''), #购货方识别号
        'buyerName':kpsq.GHFMC, #购货方名称
        'redReason':'2',#冲红原因： 1销货退回 2开票有误 3服务中止 4销售折让
        'salerTaxNum':kpsq.XHF_NSRSBH,#销方税号
        
        
        'detail': [],
    }
    kpsqGoodss = InvoiceItem.objects.filter(kpsq=kpsq)
    for idx,kpsqGoods in enumerate(kpsqGoodss):
        inXml['detail'].append({
            'goodsName': kpsqGoods.XMMC, #项目名称
            'blueDetailIndex':str(idx+1),
            'unit': (kpsqGoods.XMDW if kpsqGoods.XMDW is not None else ''), #项目单位
            'num': (kpsqGoods.XMSL if kpsqGoods.XMSL is not None else ''), #项目数量
            
            'taxExcludedPrice': (kpsqGoods.XMDJ if kpsqGoods.XMDJ is not None else '') if kpsqGoods.HSBZ in [0,'0'] else (kpsqGoods.XMDJ - (kpsqGoods.SE/kpsqGoods.XMSL) if kpsqGoods.XMDJ is not None else ''),  #项目单价
            'taxExcludedAmount': kpsqGoods.XMJE if kpsqGoods.HSBZ in [0,'0'] else kpsqGoods.XMJE - kpsqGoods.SE,#(kpsqGoods.XMDJ*kpsqGoods.XMSL if kpsqGoods.XMDJ is not None and kpsqGoods.XMSL is not None else ''),#商品金额(不含税)；带负号，精确到小数点后面两位
            'taxAmount':(kpsqGoods.SE if kpsqGoods.SE is not None else ''), #税额,
            'taxRate': kpsqGoods.SL, #税率
            'goodsCode': kpsqGoods.SPBM, #商品编码
            'specType': (kpsqGoods.GGXH if kpsqGoods.GGXH is not None else ''), #规格型号
            #'favouredPolicyFlag':,#优惠政策标识（01：简易征收 02：稀土产品 03：免税 04：不征税 05：先征后退 06：100%先征后退 07：50%先征后退 08：按3%简易征收 09：按5%简易征收 10：按5%简易征收减按1.5%计征 11：即征即退30% 12：即征即退50% 13：即征即退70% 14：即征即退100% 15：超税负3%即征即退 16：超税负8%即征即退 17：超税负12%即征即退 18：超税负6%即征即退）
         })
         
    #print inXml
    
    #inXml = { 
    #    "order" : inXml,
    #}
    return inXml
    

def fpkjInterface(app, kpsq):
    try:
    
        callcode = 'nuonuo.OpeMplatform.requestBillingNew'
        if kpsq.KPLX==2 and kpsq.InfoKind in [61,62]:
            callcode = 'nuonuo.OpeMplatform.saveInvoiceRedConfirm'
        #1.明文
        if kpsq.KPLX==1:
            inXmlInner = fpkjInxml(app, kpsq)
        elif kpsq.KPLX==2 and kpsq.InfoKind in [61,62]:
            inXmlInner = fpqdredInxml(app, kpsq)
        else:
            inXmlInner = fpkjInxml(app, kpsq)
        inXmlInner = json.dumps(inXmlInner)
        #inXmlInner = inXmlInner
        #3.调用诺诺接口开具发票
        result = callNstApi(app, callcode, inXmlInner)

        if result.get('code')!='E0000':
            if result.get('code'):
                return {
                    'returnCode':result.get('code'),
                    'returnMessage':result.get('describe', ''),
                }
            elif result.get('status'):
                return {
                    'returnCode':result.get('status'),
                    'returnMessage':result.get('message', ''),
                }
        if kpsq.KPLX==1:
            kpsq.fpqqlshFromApi = result.get('result').get('invoiceSerialNum')
            kpsq.save()
        elif kpsq.KPLX==2 and kpsq.InfoKind in [61,62]:
            kpsq.fpqqlshFromApi = result.get('result')
            kpsq.save()
        return {
            'returnCode':'0000',
            'returnMessage':'',
        }
    except Exception as e:
        return {
            'returnCode':'9999',
            'returnMessage':'%s' %e,
        }

######################################################
#发票作废 
######################################################
def fpzfInterface(app, kpsq):
    return {
        'returnCode':'9999',
        'returnMessage':'未实现的方法'
    }    


######################################################
#发票查询 诺诺与航天不同之处在于：用对方返回的“发票请求流水号”做查询
######################################################
def fpcxInterface(app, kpsq):
    returnCode = '9999'
    returnMessage = ''
    content = ''
    try:
        callCode = 'nuonuo.OpeMplatform.queryInvoiceResult'
        if kpsq.KPLX==2 and kpsq.InfoKind in [61,62] and not kpsq.requestredinv_resbillno:
            callCode = 'nuonuo.OpeMplatform.fastInvoiceRed'
        #if kpsq.KPLX==2 and kpsq.InfoKind in [61,62]:
        #    callcode = 'nuonuo.OpeMplatform.refreshInvoiceRedConfirm'
        #2.1.明文
        if kpsq.KPLX==1:
            inXmlInner = {
                "isOfferInvoiceDetail": "0",
                "orderNos": [kpsq.FPQQLSH],
                #"serialNos": [kpsq.fpqqlshFromApi]
            }
        elif kpsq.KPLX==2 and kpsq.InfoKind in [61,62] and kpsq.requestredinv_resbillno:
            inXmlInner = {
                "identity": "0",
                #"orderNos": [kpsq.FPQQLSH],
                "serialNos": [kpsq.requestredinv_resbillno]
            }
        elif kpsq.KPLX==2 and kpsq.InfoKind in [61,62] and not kpsq.requestredinv_resbillno:
            """inXmlInner = {
                "orderNo": kpsq.FPQQLSH,
                "taxNum":kpsq.XHF_NSRSBH,
                #"serialNos": [kpsq.fpqqlshFromApi]
            }"""
            inXmlInner = {
                "identity": "0",
                "billId": kpsq.FPQQLSH,
                #"serialNos": [kpsq.fpqqlshFromApi]
            }
            inXmlInner = json.dumps(inXmlInner)
            #inXmlInner = inXmlInner.encode('utf-8')
            result = callNstApi(app, "nuonuo.OpeMplatform.queryInvoiceRedConfirm", inXmlInner)
            #print(result)
            if result.get('code')!='E0000':
                return {
                    'returnCode':result.get('code'),
                    'returnMessage':result.get('describe', ''),
                }
            redqrd = result.get('result').get('list')[0] if result.get('result').get('list') else {}
            #print(redqrd)
            if not redqrd.get("billUuid"):
                return {
                    'returnCode':'9401',
                    'returnMessage':'开票处理中',
                }
            inXmlInner = {
                "orderNo":kpsq.FPQQLSH,
                "billId": redqrd.get("billId"),
                "billNo": redqrd.get("billNo"),
                "billUuid": redqrd.get("billUuid"),
                "taxNum":kpsq.XHF_NSRSBH
            }
            inXmlInner = json.dumps(inXmlInner)
            inXmlInner = inXmlInner
            result = callNstApi(app, "nuonuo.OpeMplatform.fastInvoiceRed", inXmlInner)
            if result.get('code')!='E0000':
                return {
                    'returnCode':result.get('code'),
                    'returnMessage':result.get('describe', ''),
                }
            kpsq.requestredinv_resbillno = result.get('result').get('invoiceSerialNum')
            kpsq.save()
            return fpcxInterface(app, kpsq)
        inXmlInner = json.dumps(inXmlInner)
        #inXmlInner = inXmlInner.encode('utf-8')
        
        #2.3.调用诺诺接口查询发票
        result = callNstApi(app, callCode, inXmlInner)
        if not result:
            return {
                'returnCode':'9401',
                'returnMessage':'开票处理中',
            }
        
        if result.get('code')!='E0000':
            return {
                'returnCode':result.get('code'),
                'returnMessage':result.get('describe', ''),
            }
        invoice = result['result'][0]
        
        if invoice['status'] == '2': #开票完成
            return {
                'returnCode':'0000',
                'returnMessage':'',
                'KPRQ':datetime.datetime.strptime(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(invoice['invoiceTime']/1000)), '%Y-%m-%d %H:%M:%S'),    
                'FP_DM':invoice['invoiceCode'],
                'FP_HM':invoice['invoiceNo'],
                'PDF_URL':invoice.get('pdfUrl'),
                'OFD_URL':invoice.get('ofdUrl'),
                'XML_URL':invoice.get('xmlUrl'),
                'FWM':'',
            }
        elif invoice['status'] in ['20','21']: #开票中
            return {
                'returnCode':'9401',
                'returnMessage':'开票处理中',
            }
        else:
            return {
                'returnCode':invoice['status'],
                'returnMessage':'%s%s'  %(invoice['statusMsg'],invoice['failCause'])
            }    
    
        
    except Exception as e:
        return {
            'returnCode':'8888',
            'returnMessage':'%s' %e,
        }


######################################################
#可用发票数量查询
######################################################
def kyfpslInterface(app):
    return {
        'returnCode':'9999',
        'returnMessage':'未实现的方法'
    }

def getQrCodeInterface(app,queryType):
    try:
        #1.明文
        inXmlInner = {
            'extensionNumber':'',#'分机号',
            'deptId':'',#'部门id',
            'clerkId':'',#'开票员id',
            'queryType':queryType#操作类型：0-刷新获取税局当前开票实人认证二维码 1-查询获取诺诺已获取的开票实人认证二维码； 2-刷新获取登录认证二维码 3-查询获取诺诺已获取登录认证二维码；4-发送短信验证码（短信验证码登录时） 5-查询短信验证发送状态
        }
        inXmlInner = json.dumps(inXmlInner)
        #inXmlInner = inXmlInner.encode('utf-8')
        result = callNstApi(app, 'nuonuo.OpeMplatform.getQrCode', inXmlInner)
        #print(result)
        #print(result.get('describe', '').decode('utf8'))
        if result.get('code')!='E0000':
            return {
                'returnCode':result.get('code'),
                'returnMessage':result.get('describe', ''),
            }
        if queryType in ['3','1']:
            import qrcode
            
            qrCode = result.get('result').get('qrCode')
            #print(qrCode)
            if qrCode:
                img = qrcode.make(qrCode)
                buffer = io.BytesIO()
                img.save(buffer, format="PNG")
                imgBase64 = base64.b64encode(buffer.getvalue())
            else:
                imgBase64 = ''
            #print(imgBase64)
            return {
                'returnCode':'0000',
                'returnMessage':result.get('describe', '') if qrCode else (result.get('result').get('message') if result.get('result') else json.dumps(result)),
                'imgBase64':imgBase64
            }
        return {
            'returnCode':'0000',
            'returnMessage':result.get('describe', ''),
            'result':result.get('result')
        }
    except Exception as e:
        return {
            'returnCode':'9999',
            'returnMessage':'%s' %e,
        }
        
def verifyCompleteInterface(app,queryType):
    try:
        #1.明文
        inXmlInner = {
            'extensionNumber':'',#'分机号',
            'deptId':'',#'部门id',
            'clerkId':'',#'开票员id',
            'queryType':queryType#操作类型：0-刷新获取税局当前开票实人认证二维码 1-查询获取诺诺已获取的开票实人认证二维码； 2-刷新获取登录认证二维码 3-查询获取诺诺已获取登录认证二维码；4-发送短信验证码（短信验证码登录时） 5-查询短信验证发送状态
        }
        inXmlInner = json.dumps(inXmlInner)
        result = callNstApi(app, 'nuonuo.OpeMplatform.verifyComplete', inXmlInner)
        #print(result)
        #print(result.get('describe', '').decode('utf8'))
        if result.get('code')!='E0000':
            return {
                'returnCode':result.get('code'),
                'returnMessage':result.get('describe', ''),
            }
        if not result.get('result'):
            return {
            'returnCode':'0000',
            'returnMessage':'请先进行扫码登录',
        }
        return {
            'returnCode':'0000',
            'returnMessage':result.get('result',{}).get('message',''),
        }
    except Exception as e:
        return {
            'returnCode':'9999',
            'returnMessage':'%s' %e,
        }


